package br.com.vti.leilao.view.bean;

import br.com.vti.leilao.model.business.exception.ApplicationException;
import br.com.vti.leilao.model.entity.Model;
import br.com.vti.leilao.model.entity.ModelCombo;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;


/**
 * Esta classe concentra os métodos a serem utilizados pelos beans
 *
 * @author Ednardo Rubens
 *
 * @param <E> entidade extends Model
 * @param <X> exceção extends Exception
 *
 */
public abstract class BeanImpl<E extends Model<? extends Number>, X extends ApplicationException> implements Bean<E, X> {

    private static final long serialVersionUID = -5293200680359295656L;

    private E entidade;
    private E entidadePesquisa;
    private List<E> listaEntidade;

    public void init() {
        limpar();
    }

    /* (non-Javadoc)
     * @see br.com.vti.leilao.view.bean.Bean#getEntidade()
     */
    @Override
    public E getEntidade() {
        return entidade;
    }

    /* (non-Javadoc)
     * @see br.com.vti.leilao.view.bean.Bean#setEntidade(br.com.vti.core.model.entity.Model)
     */
    @Override
    public void setEntidade(E entidade) {
        this.entidade = entidade;
    }

    /* (non-Javadoc)
     * @see br.com.vti.leilao.view.bean.Bean#getEntidadePesquisa()
     */
    @Override
    public E getEntidadePesquisa() {
        return entidadePesquisa;
    }

    /* (non-Javadoc)
     * @see br.com.vti.leilao.view.bean.Bean#setEntidadePesquisa(br.com.vti.core.model.entity.Model)
     */
    @Override
    public void setEntidadePesquisa(E entidadePesquisa) {
        this.entidadePesquisa = entidadePesquisa;
    }

    /* (non-Javadoc)
     * @see br.com.vti.leilao.view.bean.Bean#getListaEntidade()
     */
    @Override
    public List<E> getListaEntidade() {
        return listaEntidade;
    }

    /* (non-Javadoc)
     * @see br.com.vti.leilao.view.bean.Bean#setListaEntidade(java.util.List)
     */
    @Override
    public void setListaEntidade(List<E> listaEntidade) {
        this.listaEntidade = listaEntidade;
    }

    /**
     * Obtém getLogger() da classe
     * @return
     */
    protected Logger getLogger() {
        return Logger.getLogger(getClass().getName());
    }

    /**
     * Método responsável por instanciar todos os atributos
     * que possuem um contrutor padrão no objeto
     * @param objeto
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    protected void instanciarObjetosFilhos(Object objeto)
            throws InstantiationException, IllegalAccessException {
        if (objeto != null) {
            Field[] fields = objeto.getClass().getDeclaredFields();
            for (Field field : fields) {
                if (!field.getName().equals("serialVersionUID")) {
                    field.setAccessible(true);
                    boolean implementaEntidade = Model.class.isAssignableFrom(field.getType());
                    if (implementaEntidade) {
                        Object aux = field.getType().newInstance();
                        field.set(objeto, aux);
                        instanciarObjetosFilhos(aux);
                    }
                }
            }
        }
    }

    /**
     * Retorna o tipo de entidade utilizada.
     * @return classe classe da entidade
     */
    @SuppressWarnings("unchecked")
    protected Class<E> getEntityClass() {
        ParameterizedType type = (ParameterizedType) getClass().getGenericSuperclass();
        Class<E> theType = (Class<E>) type.getActualTypeArguments()[0];
        return theType;
    }

    /**
     * Método utilizado para gerar uma msg
     * @param key
     * @return mensagem de erro
     */
    protected String getSuccessMsg(String key) {
        return getBusiness().getMessageFactory().getMessage("item.sucesso.msg",
                getBusiness().getMessageFactory().getMessage(key));
    }

    /* (non-Javadoc)
     * @see br.com.vti.leilao.view.bean.Bean#getAllSelectItems()
     */
    @Override
    public Map<String, Object> getAllSelectItems() {
        Map<String, Object> items = new LinkedHashMap<String, Object>();
        items.put("SELECIONE ..", null);
        try {
            List<E> lista = getBusiness().listarParaCombo(getEntityClass());
            for (E item : lista) {
                items.put(((ModelCombo) item).getDescricao(), item);
            }
        } catch (Exception e) {
            getBusiness().getMessageFactory().addErrorMessage(e.getMessage());
        }
        return items;
    }

    /* (non-Javadoc)
     * @see br.com.vti.leilao.view.bean.Bean#salvar()
     */
    @Override
    public void salvar() {
        try {
            if (getEntidade() != null
                    && getEntidade().getId() == null) {
                getBusiness().inserir(getEntidade());
                getBusiness().getMessageFactory().addInfoMessage(getSuccessMsg("item.salvo"));
            } else {
                getBusiness().atualizar(getEntidade());
                getBusiness().getMessageFactory().addInfoMessage(getSuccessMsg("item.atualizado"));
            }
            limpar();
        } catch (ApplicationException e) {
            getBusiness().getMessageFactory().addErrorMessage(e.getMessage());
        } catch (Exception e) {
            getLogger().error(e);
        }
    }

    /* (non-Javadoc)
     * @see br.com.vti.leilao.view.bean.Bean#remover()
     */
    @Override
    public void remover() {
        try {
            getBusiness().remover(getEntidade());
            getListaEntidade().remove(getEntidade());
            getBusiness().getMessageFactory().addInfoMessage(getSuccessMsg("item.removido"));
            setEntidade(getEntityClass().newInstance());
        } catch (ApplicationException e) {
            getBusiness().getMessageFactory().addErrorMessage(e.getMessage());
        } catch (Exception e) {
            getLogger().error(e);
        }
    }

    /* (non-Javadoc)
     * @see br.com.vti.leilao.view.bean.Bean#buscar()
     */
    @Override
    public void buscar() {
        try {
            setEntidade(getBusiness().procurarPorId(this.getEntidadePesquisa()));
        } catch (ApplicationException e) {
            getBusiness().getMessageFactory().addErrorMessage(e.getMessage());
        } catch (Exception e) {
            getLogger().error(e);
        }
    }

    /* (non-Javadoc)
     * @see br.com.vti.leilao.view.bean.Bean#listar()
     */
    @Override
    public void listar() {
        List<E> lista = null;
        try {
            lista = getBusiness().listarPorExemplo(
                    getEntidadePesquisa(), 2000);
        } catch (ApplicationException e) {
            getBusiness().getMessageFactory().addErrorMessage(e.getMessage());
        } catch (Exception e) {
            getLogger().error(e);
        }
        setListaEntidade(lista);
    }

    /* (non-Javadoc)
     * @see br.com.vti.leilao.view.bean.Bean#limpar()
     */
    @Override
    public void limpar() {
        try {
            setEntidade(getEntityClass().newInstance());
            instanciarObjetosFilhos(getEntidade());
            setEntidadePesquisa(getEntityClass().newInstance());
            instanciarObjetosFilhos(getEntidadePesquisa());
            setListaEntidade(new ArrayList<E>());
        } catch (Exception e) {
            getLogger().error(e);
        }
    }
}
